﻿using System;
using System.Collections.Generic;
using AudioStudio;
using Gameplay.PVE.Utils;
using UnityEngine;
using UnityEngine.AI;
using Yoozoo.Extension;
using Yoozoo.Framework.Core;
using Yoozoo.Libs;
using Yoozoo.Managers;

namespace Gameplay.PVE.Survivor
{
    public class BattleEffect:IClass
    {
        public int indexInArray;
        public BattleEffectObject gameObject;
        public string path;
        public EEffectFollowType followType;
        public Vector3 originForward;
        public Transform followTransform;
        public bool followValid;

        public bool isSniper = false;
        
        public float disposeTime
        {
            get
            {
                var existTime = 1f;
                if (defaultDisposeTime > 0)
                {
                    existTime = defaultDisposeTime;
                }
                else
                {
                    if (gameObject.IsReady)
                    {
                        existTime = gameObject.GetExistTime();
                    }
                }
                return startTime + existTime;
            }
        }
        public EffectConfig config;
        public float defaultDisposeTime;
        public float startTime;

        public void End()
        {
            defaultDisposeTime = 0.01f;
        }

        public void LateUpdate()
        {
            if (isSniper)
            {
                gameObject?.RefreshLineTargets();
            }
            if (!followValid)
            {
                return;
            }
            if (followType == EEffectFollowType.FollowPosition || followType == EEffectFollowType.FollowPositionAndForward)
            {
                gameObject.Position = followTransform.position;
            }
        }
        
        public void OnReset()
        {
            followValid = false;
            followTransform = null;
            followType = EEffectFollowType.None;
            defaultDisposeTime = 0;
            startTime = 0;
            config = null;
            gameObject = null;
            path = null;
            isSniper = false;
        }
    }

    public class BattleDirector : IClass
    {
        public BattleGameObject gameObject;
        public EBulletShapeType directorType = EBulletShapeType.Default;
        public float disposeTime;
        public float storageTime;
        public float storageStartTime;
        public GameObject storageObj;
        public Action<BattleGameObject> updateAction;
        
        public void OnReset()
        {
            storageTime = 0;
            storageStartTime = 0;
            storageObj = null;
            directorType = EBulletShapeType.Default;
            updateAction = null;
            gameObject = null;
            disposeTime = 0;
        }

        public void Update()
        {
            updateAction?.Invoke(gameObject);
            if (storageTime > 0)
            {
                var temp = TimeManager.time - storageStartTime;
                if (storageObj == null)
                {
                    storageObj = gameObject.GetChild(0);
                }
                var pro = temp / storageTime;
                setStorageProgress(pro);
            }
        }

        void setStorageProgress(float value)
        {
            var pro = value > 1 ? 1 : value;
            if (!storageObj)
            {
                return;
            }
            switch (directorType)
            {
                case EBulletShapeType.Rect:
                    storageObj.transform.SetScaleZ(pro);
                    break;
                case EBulletShapeType.Circle:
                case EBulletShapeType.Sector:
                    storageObj.transform.SetScale(pro);
                    break;
            }
        }
    }
    
    public class BattleEffectManager: BattleSingleton<BattleEffectManager>
    {
        private List<BattleEffect> effectList = new List<BattleEffect>();
        private List<BattleDirector> directorList = new List<BattleDirector>();
        public void CreateEffectAutoDispose(int effectId, Vector3 position, Vector3 forward,float autoDisposeTime = -1,Transform parent = null)
        {
            var originParent = parent;
            if (parent == null)
            {
                parent = SurvivorManager.Instance.Root;
            }
            var effect = CreateEffect(effectId);
            if (effect != null)
            {
                var localPosition = position;
                if (effect.config.ground_effect == 1)
                {
                    localPosition.y = GetGroundHeight(localPosition);
                }
                
                var offset = GetPositionOffset(effect.config,forward);
                effect.defaultDisposeTime = autoDisposeTime;
                effect.gameObject.Parent = effect.followType == EEffectFollowType.SetParent ? parent : SurvivorManager.Instance.Root;
                if (effect.followType != EEffectFollowType.None)
                {
                    if (effect.followType == EEffectFollowType.SetParent)
                    {
                        effect.gameObject.LocalPosition = Vector3.zero;
                        effect.gameObject.LocalEulerAngle = Vector3.zero;
                    }
                    else if (effect.followType == EEffectFollowType.FollowPositionAndForward || effect.followType == EEffectFollowType.FollowPosition)
                    {
                        effect.followValid = false;
                        if (originParent)
                        {
                            effect.followTransform = originParent;
                            effect.followValid = true;
                            effect.gameObject.Position = originParent.position;
                            if (effect.followType == EEffectFollowType.FollowPositionAndForward)
                            {
                                effect.gameObject.Forward = originParent.forward;
                            }
                        }
                        else
                        {
                            effect.gameObject.Position = localPosition + offset;
                            effect.gameObject.Forward = forward;
                        }
                    }
                }
                else
                {
                    effect.gameObject.Position = localPosition + offset;
                    effect.gameObject.Forward = forward;
                }
                var children = effect.config.children;
                if (children != null && children.Count > 0)
                {
                    for (int i = 0; i < children.Count; i++)
                    {
                        CreateEffectAutoDispose(children[i],position,forward,autoDisposeTime,parent);
                    }
                }
            }
        }

        public BattleEffect CreateEffect(int effectId)
        {
            if (!SurvivorManager.isShowEffect)
            {
                return null;
            }
            var effectConfig = BattleConfigManager.Instance.GetEffectConfig(effectId);
            if (effectConfig == null)
            {
                return null;
            }
            var effect = ClassManager.Get<BattleEffect>();
            effect.gameObject = BattlePoolManager.Instance.GetEffect(effectConfig.res_id_h);
            effect.indexInArray = effectList.Count;
            effect.config = effectConfig;
            effect.followType = (EEffectFollowType)effectConfig.set_parent;
            effect.startTime = TimeManager.time;

            if (!string.IsNullOrEmpty(effectConfig.sound_name))
            {
                AudioManager.PlaySound(effectConfig.sound_name);
            }
            effectList.Add(effect);
            return effect;
        }

        public void RemoveEffect(BattleEffect effect)
        {
            effect.End();
        }

        public void Update()
        {
            for (int i = 0; i < effectList.Count; i++)
            {
                var cell = effectList[i];
                if (cell.disposeTime > 0 && TimeManager.time > cell.disposeTime)
                {
                    //跟最后一个元素交换位置
                    if (i != effectList.Count - 1)
                    {
                        effectList[i] = effectList[effectList.Count - 1];
                        effectList[i].indexInArray = i;
                        i--;
                    }
                    effectList.RemoveAt(effectList.Count - 1);
                    BattlePoolManager.Instance.ReleaseEffect(cell.gameObject);
                    ClassManager.Free(cell);
                }
            }

            for (int i = 0; i < directorList.Count; i++)
            {
                var cell = directorList[i];
                if (cell.disposeTime > 0 && TimeManager.time > cell.disposeTime)
                {
                    BattlePoolManager.Instance.Release(cell.gameObject);
                    ClassManager.Free(cell);
                    directorList.RemoveAt(i);
                    i--;
                }
                else
                {
                    cell.Update();
                }
            }
        }

        public void LateUpdate()
        {
            for (int i = 0; i < effectList.Count; i++)
            {
                var cell = effectList[i];
                cell.LateUpdate();
            }
        }
        
        public void CreateChainEffect(int effectId, BattleUnit target1,BattleUnit target2)
        {
            var effect = CreateEffect(effectId);
            if (effect != null)
            {
                effect.gameObject.SetChainTargets(target1.AimPoint, target2.AimPoint);
            }
        }

        public void CreateSniperLineEffect(int effectId, Transform parent,Transform target,float duration = 2)
        {
            var effect = CreateEffect(effectId);
            if (effect != null)
            {
                effect.gameObject.Parent = parent;
                effect.gameObject.LocalPosition = Vector3.zero;
                effect.gameObject.SetLineTargets(parent,target);
                effect.defaultDisposeTime = duration;
                effect.isSniper = true;
            }
        }
        
        private Vector3 GetPositionOffset(EffectConfig config,Vector3 forward)
        {
            if (config.position_offset_min.Count < 3 || config.position_offset_max.Count < 3)
            {
                return Vector3.zero;
            }
            var min = new Vector3(config.position_offset_min[0], config.position_offset_min[1], config.position_offset_min[2]);
            var max = new Vector3(config.position_offset_max[0], config.position_offset_max[1], config.position_offset_max[2]);
            var xMax = Math.Max(min.x, max.x);
            var yMax = Math.Max(min.y, max.y);
            var zMax = Math.Max(min.z, max.z);
            var xMin = Math.Min(min.x, max.x);
            var yMin = Math.Min(min.y, max.y);
            var zMin = Math.Min(min.z, max.z);
            var angle = PveUtils.GetAngle(Vector3.forward, forward);
            var result = new Vector3(UnityEngine.Random.Range(xMin, xMax), UnityEngine.Random.Range(yMin, yMax), UnityEngine.Random.Range(zMin, zMax));
            return Quaternion.AngleAxis(angle,
                Vector3.up) * result;
        }

        public void ShowSkillDirector(Vector3 position, Vector3 forward, float duration, int shapeType,
            float[] shapeParams, float StrogetTime = 0,Action<BattleGameObject> updateAction = null)
        {
            //BattleDebug.LogError("显示指示器 " + shapeType + " " + position);
            var director = ClassManager.Get<BattleDirector>();
            var resId = EBattleResId.DirectorCircle;
            var scale = Vector3.one;
            if (shapeType == (int)EBulletShapeType.Circle)
            {
                resId = StrogetTime == 0 ? EBattleResId.DirectorCircle : EBattleResId.DirectorCircleStroge;
                var shapeParam1 = 1f;
                if (shapeParams.Length > 0)
                {
                    shapeParam1 = shapeParams[0];
                }
                else
                {
                    BattleDebug.LogError("指示器参数错误 ");
                }

                scale = new Vector3(shapeParam1, shapeParam1, shapeParam1);
            }
            else if (shapeType == (int)EBulletShapeType.Sector)
            {
                var shapeParam1 = 401;
                var shapeParam2 = 1f;
                if (shapeParams.Length > 1)
                {
                    shapeParam1 = (int)shapeParams[1];
                    shapeParam2 = shapeParams[0];
                }
                else
                {
                    BattleDebug.LogError("指示器参数错误 ");
                }
                resId = BattleHelper.GetSectorDirectorResId(shapeParam1,StrogetTime > 0);
                scale = new Vector3(shapeParam2, shapeParam2, shapeParam2);
            }
            else if (shapeType == (int)EBulletShapeType.Rect)
            {
                resId = StrogetTime == 0 ? EBattleResId.DirectorRect : EBattleResId.DirectorRectStroge;
                var shapeParam1 = 1f;
                var shapeParam2 = 1f;
                if (shapeParams.Length > 1)
                {
                    shapeParam1 = shapeParams[0];
                    shapeParam2 = shapeParams[1];
                }
                else
                {
                    BattleDebug.LogError("指示器参数错误 ");
                }
                scale = new Vector3(shapeParam1, 1, shapeParam2);
            }
            director.directorType = (EBulletShapeType)shapeType;
            director.gameObject = BattlePoolManager.Instance.Get(resId);
            position.y = GetGroundHeight(position);
            director.gameObject.Position = position;
            director.gameObject.Forward = forward;
            director.gameObject.Scale = scale;
            director.updateAction = updateAction;
            if (StrogetTime > 0)
            {
                director.storageStartTime = TimeManager.time;
                director.storageTime = StrogetTime;
            }
            director.disposeTime = TimeManager.time + duration;
            directorList.Add(director);
        }
        
        public static float GetGroundHeight(Vector3 position)
        {
            if (NavMesh.SamplePosition(position + new Vector3(0, 1, 0), out var hit, 100, NavMesh.AllAreas))
            {
                return hit.position.y + 0.01f;
            }
            else
            {
                return 0.01f;
            }
        }
        
        public override void Dispose()
        {
            for (int i = 0; i < effectList.Count; i++)
            {
                var cell = effectList[i];
                BattlePoolManager.Instance.ReleaseEffect(cell.gameObject);
                ClassManager.Free(cell);
            }
            effectList.Clear();
            for (int i = 0; i < directorList.Count; i++)
            {
                var cell = directorList[i];
                BattlePoolManager.Instance.Release(cell.gameObject);
                ClassManager.Free(cell);
            }
            directorList.Clear();
        }
    }
}
